package com.hmall.item.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.common.dto.PageDTO;
import com.hmall.common.dto.SearchDTO;
import com.hmall.common.vo.SearchVO;
import com.hmall.item.mapper.ItemMapper;
import com.hmall.item.pojo.Item;
import com.hmall.item.pojo.ItemDoc;
import com.hmall.item.service.IItemService;
import lombok.RequiredArgsConstructor;

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor //不用@Autowired每次注入
public class ItemService extends ServiceImpl<ItemMapper, Item> implements IItemService {


    private ItemMapper itemMapper;

    private final RabbitTemplate rabbitTemplate;

    private final RestHighLevelClient client;

    @Override
    public PageDTO listPage(Integer page, Integer size) {
        Page<Item> p = new Page<>();
        p.setCurrent(page);
        p.setSize(size);
        Page<Item> itemPage = page(p);
        return PageDTO.<Item>builder()
                .total(itemPage.getTotal())
                .list(itemPage.getRecords())
                .build();
    }

    /*根据id查询商品*/
    @Override
    public Item getItemById(Long id) {
        return itemMapper.selectById(id);
    }

    /*新增商品 */
    @Override
    public boolean addItem(Item item) {
        return save(item);
    }

    /*商品上下架*/
    @Override
    public void updateItemStatus(Long id, int status) {
        update().set("status", status).eq("id", id).update();
    }

    /*修改商品*/
    @Override
    public boolean updateItem(Item item) {
        return updateById(item);
    }

    /*删除商品*/
    @Override
    public boolean deleteItemById(Long id) {
        return removeById(id);
    }

    @Override
    public void stock(Long itemId, Integer num) {
        Item item = itemMapper.selectById(itemId);
        Integer stock = item.getStock();
        item.setStock(stock - num);
        itemMapper.insert(item);
    }

    public SearchVO filter(SearchDTO searchDTO) {
        List<String> categoryList = new ArrayList<>();
        List<String> brandList = new ArrayList<>();
        //创建请求对象
        SearchRequest request = new SearchRequest("items");
        String brandAggName = "brandAgg";
        String categoryAggName = "categoryName";
        //封装请求参数
        request.source().aggregation(AggregationBuilders.terms(brandAggName).field("brand").size(22));
        request.source().aggregation(AggregationBuilders.terms(categoryAggName).field("category").size(20));
        //发起请求
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //解析品牌聚合结果
            Terms brandTerms = response.getAggregations().get(brandAggName);
            List<? extends Terms.Bucket> brandBuckets = brandTerms.getBuckets();
            for (Terms.Bucket bucket : brandBuckets) {
                String brand = bucket.getKeyAsString();
                brandList.add(brand);
            }
            //解析分类聚合结果
            Terms categoryTerms = response.getAggregations().get(categoryAggName);
            List<? extends Terms.Bucket> categoryBuckets = categoryTerms.getBuckets();
            for (Terms.Bucket categoryBucket : categoryBuckets) {
                String category = categoryBucket.getKeyAsString();
                categoryList.add(category);
            }
            return SearchVO.builder()
                    .category(categoryList)
                    .brand(brandList)
                    .build();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }



    public PageDTO<ItemDoc> listWithCondition(SearchDTO searchDTO) {
        //创建搜索请求对象
        SearchRequest request = new SearchRequest("items");
        List<ItemDoc> itemDocList = new ArrayList<>();
        //封装请求参数
        String key = searchDTO.getKey();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (StrUtil.isNotBlank(key)) {
            //按照关键字过滤
            boolQuery.must(QueryBuilders.matchQuery("name", key));
        } else {//查询全部
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        //过滤分类
        if (StrUtil.isNotBlank(searchDTO.getCategory())) {
            boolQuery.filter(QueryBuilders.termQuery("category", searchDTO.getCategory()));
        }
        //过滤品牌
        if (StrUtil.isNotBlank(searchDTO.getBrand())) {
            boolQuery.filter(QueryBuilders.termQuery("brand", searchDTO.getBrand()));
        }
        //过滤价格
        // >= 最小值
        if (searchDTO.getMinPrice() != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(searchDTO.getMinPrice() * 100));
        }

        // <= 最大值
        if (searchDTO.getMinPrice() != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(searchDTO.getMaxPrice() * 100));
        }

        FunctionScoreQueryBuilder scoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQuery,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(10))
                });
        scoreQueryBuilder.boostMode(CombineFunction.MULTIPLY);

        request.source().query(scoreQueryBuilder);

        //排序
        if (StrUtil.isNotBlank(searchDTO.getSortBy()) && !"default".equals(searchDTO.getSortBy())) {
            request.source().sort(searchDTO.getSortBy(), SortOrder.DESC);
        }
        //分页
        int from = (searchDTO.getPage() - 1) * searchDTO.getSize();
        request.source().from(from).size(searchDTO.getSize());

        //高亮
        request.source().highlighter(new HighlightBuilder()
                .field("name")
                .preTags("<em>")
                .postTags("</em>")
                .requireFieldMatch(false)
        );
        try {
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            long total = response.getHits().getTotalHits().value;
            SearchHits hits = response.getHits();
            SearchHit[] searchHits = hits.getHits();
            if (searchHits != null && searchHits.length > 0) {
                for (SearchHit searchHit : searchHits) {
                    String itemJson = searchHit.getSourceAsString();
                    ItemDoc itemDoc = JSONUtil.toBean(itemJson, ItemDoc.class);

                    //高亮字段
                    Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                    if (CollUtil.isNotEmpty(highlightFields)) {
                        HighlightField highlightField = highlightFields.get("name");
                        Text[] fragments = highlightField.getFragments();
                        if (ArrayUtil.isNotEmpty(fragments)) {
                            itemDoc.setName(fragments[0].toString());
                        }
                    }
                    itemDocList.add(itemDoc);
                }
            }
            //封装响应结果
            return PageDTO.of(total, itemDocList);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

